#include <algorithm>
#include <fstream>
#include <stdlib.h>

#include "CommutativityGenerator.h"
#include "DistributivityGenerator.h"
#include "PhysicalOperatorsGenerator.h"
#include "SearchEngine.h"

double CPU_speed;
double RAM_speed;
double HDD_access_time;
double NET_transmission_time;
double Nodes_count;

SearchEngine::SearchEngine(std::string n, Query *query)
	: name(n), query(query), min_query(-1), min_cost(-1)
{
	qOut = new QueryOutput;
	timer = new Timer();	

	current = 0;
	queryNumber = 1;

	qOut->beginOutput(getName());

	timer->start();
		
	getGenerators(query);
	assert(!generators.empty());
	currGenerator = 0;
	prepareQuery(query);
}

SearchEngine::~SearchEngine()
{
	for (Generators::iterator i = generators.begin(); i != generators.end(); i++)
	{
		delete *i;
	}

	timer->end();

	qOut->endOutput(getName());

	std::ofstream info("output/information.txt", std::ios::out | std::ios::app);
	info << "Elapsed time on \'" << name << "\' algorithm is " << timer->getMilliseconds() << " ms.\n";
	info.close();

	delete timer;
	delete qOut;
}

std::string SearchEngine::getName()
{
	return name;
}

bool sort_by_cost(QueryInfo *qi1, QueryInfo *qi2)
{
	return qi1->cost < qi2->cost;
}

void SearchEngine::findCosts(QueryStorage *qs)
{
	for (std::vector <QueryInfo *> :: iterator i = qs->queries.begin(); i != qs->queries.end(); i++)
	{
		if ((*i)->cost != -1)
		{
			continue;
		}

		CostInfo *ci = new CostInfo(0, 0);
		queryCost((*i)->query, ci);

		(*i)->cost = ci->cost;

		delete ci;
	}

	for (std::vector <QueryInfo *> :: iterator i = qs->queries.begin(); i != qs->queries.end(); i++)
	{
		if (min_cost == -1)
		{
			min_cost = (*i)->cost;
			min_query = (*i)->number;
		}
		else
		{
			if (min_cost > (*i)->cost)
			{
				min_cost = (*i)->cost;
				min_query = (*i)->number;
			}
		}
	}	
}

static bool sortGenerators(Generator *g1, Generator *g2)
{
	return *g1 < *g2;
}

void SearchEngine::getGenerators(Query *query)
{
	int prop = 1;
	int q_prop = getAllQueryProperties(query);

	generators.push_back(new PhysicalOperatorsGenerator(PROPERTY_EMPTY, 30));

	while (prop <= q_prop)
	{
		if (prop & q_prop)
		{
			switch (prop)
			{
			case PROPERTY_COMMUTATIVITY:
				generators.push_back(new CommutativityGenerator(PROPERTY_COMMUTATIVITY, 20));
				break;
			case PROPERTY_DISTRIBUTIVITY:
				//generators.push_back(new DistributivityGenerator(PROPERTY_DISTRIBUTIVITY, 10));
				break;
			/*
			 * New generators must be added here.
			*/
			default:
				;
			}
		}

		prop <<= 1;
	}

	std::sort(generators.begin(), generators.end(), sortGenerators);
}

int SearchEngine::getAllQueryProperties(Query *query)
{
	int result = query->node->op->getOperatorProperties();

	for (std::vector <Query *> :: iterator i = query->lists.begin(); i != query->lists.end(); i++)
	{
		result |= getAllQueryProperties(*i);
	}

	return result;
}

void SearchEngine::prepareQuery(Query *query)
{
	for (std::vector<Query *>::iterator i = query->lists.begin(); i != query->lists.end(); i++)
	{
		prepareQuery(*i);
	}

	if (query->node->physOp)
	{
		return;
	}

	std::vector<PhysicalOperator *> list;
	findPhysicalOperatorbyName(query->node->op->getName(), list);

	assert(!list.empty());

	query->node->physOp = list.front();
}
